/*
 * HashTableFunctions.java
 *
 * Copyright (C) 2002-2006 Peter Graves
 * $Id: HashTableFunctions.java 12290 2009-11-30 22:28:50Z vvoutilainen $
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent
 * modules, and to copy and distribute the resulting executable under
 * terms of your choice, provided that you also meet, for each linked
 * independent module, the terms and conditions of the license of that
 * module.  An independent module is a module which is not derived from
 * or based on this library.  If you modify this library, you may extend
 * this exception to your version of the library, but you are not
 * obligated to do so.  If you do not wish to do so, delete this
 * exception statement from your version.
 */
package org.armedbear.lisp;

import static org.armedbear.lisp.Lisp.*;

/**
 * Description of the Class
 * 
 * @author Administrator
 */
public final class HashTableFunctions {
  private static final LispObject FUNCTION_EQ = Symbol.EQ.getSymbolFunction();
  private static final LispObject FUNCTION_EQL = Symbol.EQL.getSymbolFunction();
  private static final LispObject FUNCTION_EQUAL = Symbol.EQUAL.getSymbolFunction();
  private static final LispObject FUNCTION_EQUALP = Symbol.EQUALP.getSymbolFunction();

  // ### %make-hash-table
  private static final Primitive _MAKE_HASH_TABLE = new Primitive("%make-hash-table", PACKAGE_SYS, false) {

    public LispObject execute(LispObject test, LispObject size, LispObject rehashSize, LispObject rehashThreshold) {
      return _MAKE_HASH_TABLE_execute(test, size, rehashSize, rehashThreshold);
    }
  };

  static final public LispObject _MAKE_HASH_TABLE_execute(LispObject test, LispObject size, LispObject rehashSize,
      LispObject rehashThreshold) {
    final int n = Fixnum.getValue(size);
    if (test == FUNCTION_EQL || test == NIL) {
      return new EqlHashTable(n, rehashSize, rehashThreshold);
    }
    if (test == FUNCTION_EQ) {
      return new EqHashTable(n, rehashSize, rehashThreshold);
    }
    if (test == FUNCTION_EQUAL) {
      return new EqualHashTable(n, rehashSize, rehashThreshold);
    }
    if (test == FUNCTION_EQUALP) {
      return new EqualpHashTable(n, rehashSize, rehashThreshold);
    }
    return error(new LispError("Unsupported test for MAKE-HASH-TABLE: " + test.writeToString()));
  }

  // ### gethash key hash-table &optional default => value, present-p
  private static final Primitive GETHASH = new Primitive(Symbol.GETHASH, "key hash-table &optional default") {

    public LispObject execute(LispObject key, LispObject ht) {
      return GETHASH_execute(key, ht);
    }

    public LispObject execute(LispObject key, LispObject ht, LispObject defaultValue) {
      return GETHASH_execute(key, ht, defaultValue);
    }
  };

  static final public LispObject GETHASH_execute(LispObject key, LispObject ht) {
    return checkHashTable(ht).gethash(key);
  }

  static final public LispObject GETHASH_execute(LispObject key, LispObject ht, LispObject defaultValue) {
    return checkHashTable(ht).gethash(key, defaultValue);
  }

  // ### gethash1 key hash-table => value
  private static final Primitive GETHASH1 = new Primitive(Symbol.GETHASH1, "key hash-table") {

    public LispObject execute(LispObject first, LispObject second) {
      return GETHASH1_execute(first, second);
    }
  };

  static final public LispObject GETHASH1_execute(LispObject first, LispObject second) {
    final HashTable ht = checkHashTable(second);
    synchronized (ht) {
      final LispObject value = ht.get(first);
      return value != null ? value : NIL;
    }
  }

  // ### puthash key hash-table new-value &optional default => value
  private static final Primitive PUTHASH = new Primitive(Symbol.PUTHASH, "key hash-table new-value &optional default") {

    public LispObject execute(LispObject key, LispObject ht, LispObject value) {
      return PUTHASH_execute(key, ht, value);
    }

    public LispObject execute(LispObject key, LispObject ht, LispObject ignored, LispObject value) {
      return PUTHASH_execute(key, ht, ignored, value);
    }
  };

  static final public LispObject PUTHASH_execute(LispObject key, LispObject ht, LispObject value) {
    return checkHashTable(ht).puthash(key, value);
  }

  static final public LispObject PUTHASH_execute(LispObject key, LispObject ht, LispObject ignored, LispObject value) {
    return checkHashTable(ht).puthash(key, value);
  }

  // remhash key hash-table => generalized-boolean
  private static final Primitive REMHASH = new Primitive(Symbol.REMHASH, "key hash-table") {

    public LispObject execute(LispObject key, LispObject ht) {
      return REMHASH_execute(key, ht);
    }
  };

  static final public LispObject REMHASH_execute(LispObject key, LispObject ht) {
    return checkHashTable(ht).remhash(key);
  }

  // ### clrhash hash-table => hash-table
  private static final Primitive CLRHASH = new Primitive(Symbol.CLRHASH, "hash-table") {

    public LispObject execute(LispObject ht) {
      return CLRHASH_execute(ht);
    }
  };

  static final public LispObject CLRHASH_execute(LispObject ht) {
    checkHashTable(ht).clear();
    return ht;
  }

  // ### hash-table-count
  private static final Primitive HASH_TABLE_COUNT = new Primitive(Symbol.HASH_TABLE_COUNT, "hash-table") {

    public LispObject execute(LispObject arg) {
      return HASH_TABLE_COUNT_execute(arg);
    }
  };

  static final public LispObject HASH_TABLE_COUNT_execute(LispObject arg) {
    return Fixnum.getInstance(checkHashTable(arg).getCount());
  }

  // ### sxhash object => hash-code
  private static final Primitive SXHASH = new Primitive(Symbol.SXHASH, "object") {

    public LispObject execute(LispObject arg) {
      return SXHASH_execute(arg);
    }
  };

  static final public LispObject SXHASH_execute(LispObject arg) {
    return Fixnum.getInstance(arg.sxhash());
  }

  // ### psxhash object => hash-code
  // For EQUALP hash tables.
  private static final Primitive PSXHASH = new Primitive("psxhash", PACKAGE_SYS, true, "object") {

    public LispObject execute(LispObject arg) {
      return PSXHASH_execute(arg);
    }
  };

  static final public LispObject PSXHASH_execute(LispObject arg) {
    return Fixnum.getInstance(arg.psxhash());
  }

  // ### hash-table-p
  private static final Primitive HASH_TABLE_P = new Primitive(Symbol.HASH_TABLE_P, "object") {

    public LispObject execute(LispObject arg) {
      return HASH_TABLE_P_execute(arg);
    }
  };

  static final public LispObject HASH_TABLE_P_execute(LispObject arg) {
    return arg instanceof HashTable ? T : NIL;
  }

  // ### hash-table-entries
  private static final Primitive HASH_TABLE_ENTRIES = new Primitive("hash-table-entries", PACKAGE_SYS, false) {

    public LispObject execute(LispObject arg) {
      return HASH_TABLE_ENTRIES_execute(arg);
    }
  };

  static final public LispObject HASH_TABLE_ENTRIES_execute(LispObject arg) {
    return checkHashTable(arg).ENTRIES();
  }

  // ### hash-table-test
  private static final Primitive HASH_TABLE_TEST = new Primitive(Symbol.HASH_TABLE_TEST, "hash-table") {

    public LispObject execute(LispObject arg) {
      return HASH_TABLE_TEST_execute(arg);
    }
  };

  static final public LispObject HASH_TABLE_TEST_execute(LispObject arg) {
    return checkHashTable(arg).getTest();
  }

  // ### hash-table-size
  private static final Primitive HASH_TABLE_SIZE = new Primitive(Symbol.HASH_TABLE_SIZE, "hash-table") {

    public LispObject execute(LispObject arg) {
      return HASH_TABLE_SIZE_execute(arg);
    }
  };

  static final public LispObject HASH_TABLE_SIZE_execute(LispObject arg) {
    return Fixnum.getInstance(checkHashTable(arg).getSize());
  }

  // ### hash-table-rehash-size
  private static final Primitive HASH_TABLE_REHASH_SIZE = new Primitive(Symbol.HASH_TABLE_REHASH_SIZE, "hash-table") {

    public LispObject execute(LispObject arg) {
      return HASH_TABLE_REHASH_SIZE_execute(arg);
    }
  };

  static final public LispObject HASH_TABLE_REHASH_SIZE_execute(LispObject arg) {
    return checkHashTable(arg).getRehashSize();
  }

  // ### hash-table-rehash-threshold
  private static final Primitive HASH_TABLE_REHASH_THRESHOLD = new Primitive(Symbol.HASH_TABLE_REHASH_THRESHOLD,
      "hash-table") {

    public LispObject execute(LispObject arg) {
      return HASH_TABLE_REHASH_THRESHOLD_execute(arg);
    }
  };

  static final public LispObject HASH_TABLE_REHASH_THRESHOLD_execute(LispObject arg) {
    return checkHashTable(arg).getRehashThreshold();
  }

  // ### maphash
  private static final Primitive MAPHASH = new Primitive(Symbol.MAPHASH, "function hash-table") {

    public LispObject execute(LispObject first, LispObject second) {
      return MAPHASH_execute(first, second);
    }
  };

  static final public LispObject MAPHASH_execute(LispObject first, LispObject second) {
    return checkHashTable(second).MAPHASH(first);
  }

  /**
   * Description of the Method
   * 
   * @param ht
   *          Description of Parameter
   * @return Description of the Returned Value
   */
  protected static HashTable checkHashTable(LispObject ht) {
    if (ht instanceof HashTable) {
      return (HashTable) ht;
    }
    type_error(ht, Symbol.HASH_TABLE);
    return null;
  }

  static {
    InlinedPrimitiveRegistry.inlineStaticsNow(HashTableFunctions.class);
  }

}
